package dao

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"micro.user.auth.server/common"
	"micro.user.auth.server/config"
	"micro.user.auth.server/database"
	"micro.user.auth.server/utils"
	"net/http"
	"time"
)

const (
	WxTokenExpireTime = 7000
)

type Code2SessionResp struct {
	Openid     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionId    string `json:"unionid"`
	ErrCode    int    `json:"err_code"`
	ErrMsg     string `json:"err_msg"`
}

type Response struct {
	Openid     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionId    string `json:"unionid"`
}

type accessTokenRsp struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	ErrCode     int    `json:"err_code"`
	ErrMsg      string `json:"err_msg"`
}

func GetWxUserInfo(code string) (Response, error) {
	var res Response
	var response Code2SessionResp

	u := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", config.MiniProgramConf.MiniProgram.AppID, config.MiniProgramConf.MiniProgram.AppSecret, code)
	log.Println("url:", u)

	resp, err := http.Get(u)
	if err != nil {
		return res, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return res, err
	}

	if err := json.Unmarshal(body, &response); err != nil {
		return res, err
	}

	if response.ErrCode != 0 {
		err := errors.New("code2Session get error")
		return res, err
	}

	res.UnionId = response.UnionId
	res.SessionKey = response.SessionKey
	res.Openid = response.Openid

	return res, nil
}

func GetWxAccessToken(key string) (*accessTokenRsp, error) {
	var rsp = &accessTokenRsp{}
	redis := database.GetRedis()
	if redis == nil {
		return rsp, common.RedisIsNil
	}
	// 先在Redis中设置空token，防止大量请求获取token导致http请求超时
	redis.SetNX(key, "", time.Minute)

	defer func(rsp *accessTokenRsp) {
		if rsp.AccessToken != "" {
			// 随机数，防止大量token过期
			redis.SetNX(key, rsp.AccessToken, time.Second*time.Duration(WxTokenExpireTime+utils.RandNum()))
		}
	}(rsp)

	u := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", config.MiniProgramConf.MiniProgram.AppID, config.MiniProgramConf.MiniProgram.AppSecret)
	log.Println("url:", u)

	resp, err := http.Get(u)
	if err != nil {
		return rsp, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return rsp, err
	}

	if err := json.Unmarshal(body, &rsp); err != nil {
		return rsp, err
	}

	if rsp.ErrCode != 0 {
		err := errors.New("wx user token")
		return rsp, err
	}

	return rsp, nil
}

func GetWxAccessTokenFromRedis(key string) (string, error) {
	redis := database.GetRedis()
	if redis == nil {
		return "", common.RedisIsNil
	}

	result := redis.Get(key)
	if result.Err() != nil {
		return "", result.Err()
	}

	return result.Val(), nil
}
